package main

import (
	"context"
	"errors"
	"fmt"
	"time"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

func main() {
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	// 连接mongodb获取mongo.Client
	mc, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://root:123456@192.168.1.2:27018/?authSource=admin&readPreference=primary&ssl=false"))
	if err != nil {
		panic(err)
	}

	// 当程序结束前断开与mongodb的连接
	defer func() {
		if err = mc.Disconnect(ctx); err != nil {
			panic(err)
		}
	}()

	col := mc.Database("coolcar").Collection("account")

	insertRows(ctx, col)
	findRow(ctx, col)
	findRows(ctx, col)
	findRowAndUpdate(ctx, col, "345")
	findRowAndReplace(ctx, col)

}

// findRowAndReplace 查询并替换
func findRowAndReplace(ctx context.Context, col *mongo.Collection) {
	objID, err := primitive.ObjectIDFromHex("62a84fdb6077a2b170f06608")
	if err != nil {
		panic(err)
	}
	res := col.FindOneAndReplace(ctx, bson.M{
		"_id":     objID,
		"open_id": "123",
	}, bson.M{
		"open_id": "123",
	})
	if res != nil {
		if res.Err() == mongo.ErrNoDocuments {
			fmt.Printf("cannot find document, err: %v\n", res.Err())
		}
	}
}

// findRowAndUpdate 查询并更新
func findRowAndUpdate(ctx context.Context, col *mongo.Collection, openID string) {
	// 选择条件
	filter := bson.M{
		"open_id": openID,
	}
	// 更新的内容
	update := bson.M{
		"$set": bson.M{
			"open_id": openID,
		},
	}
	// SetReturnDocument设置返回的文档是更新后的文档。默认:options.Before
	// SetUpsert设置如果过滤器匹配不到文档,则插入新的文档。默认:false
	opts := options.FindOneAndUpdate().SetReturnDocument(options.After).SetUpsert(true)

	var row bson.M
	// var row struct {
	// 	ID      primitive.ObjectID `bson:"_id"`
	// 	OpendID string             `bson:"open_id"`
	// }
	err := col.FindOneAndUpdate(ctx, filter, update, opts).Decode(&row)
	if err != nil {
		// 判断错误类型是否查不到文档
		if errors.Is(err, mongo.ErrNoDocuments) {
			fmt.Printf("No documents%#v\n", err)
			return
		}
		panic(err)
	}
	fmt.Println(row)
}

func findRow(ctx context.Context, col *mongo.Collection) {
	// primitive.ObjectID是bson格式的ObjectID类型
	// bson标签,表示该字段在MongoDB的文档中的字段名是`_id`
	var row struct {
		ID     primitive.ObjectID `bson:"_id"`
		OpenID string             `bson:"open_id"`
	}

	// FindOne() 返回的是 *mongo.SingleResult 需要.Decode()才能获取返回内容
	err := col.FindOne(ctx, bson.M{
		"open_id": "123",
	}).Decode(&row)

	if err != nil {
		panic(err)
	}
	fmt.Printf("%+v\n", row)
}

func findRows(ctx context.Context, col *mongo.Collection) {
	cursor, err := col.Find(ctx, bson.M{})
	if err != nil {
		panic(err)
	}
	for cursor.Next(ctx) {
		var row struct {
			ID     primitive.ObjectID `bson:"_id"`
			OpenID string             `bson:"open_id"`
		}
		err = cursor.Decode(&row)
		if err != nil {
			panic(err)
		}
		fmt.Printf("%+v\n", row)
	}
}

func insertRows(ctx context.Context, col *mongo.Collection) {
	res, err := col.InsertMany(ctx, []interface{}{
		// bson: 二进制表示形式,MongoDB主要用该格式进行存储和网络传输
		// bson.M: map[string]interface{}
		bson.M{
			"open_id": "789",
		},
		bson.M{
			"open_id": "012",
		},
	})
	if err != nil {
		panic(err)
	}

	fmt.Printf("%+v\n", res)
}
